package com.mymoney.persistence;

import java.util.Stack;
import java.util.UUID;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

import com.mymoney.exceptions.MyMoneyException;
import com.mymoney.persistence.managers.CategoryManager;
import com.mymoney.persistence.managers.CurrencyManager;
import com.mymoney.persistence.managers.DeviceManager;
import com.mymoney.persistence.managers.ItemManager;
import com.mymoney.persistence.managers.LocationManager;
import com.mymoney.persistence.managers.PlatformManager;
import com.mymoney.persistence.managers.StoreManager;


public class PersistenceContext {

	private static final String PERSISTENCE_UNIT_NAME = "transactions-optional";

	private static EntityManagerFactory entityManagerFactory = null;


	public static PersistenceContext getPersistenceContext() {
		if (entityManagerFactory == null) {
			init();
		}
		return new PersistenceContext(entityManagerFactory.createEntityManager());
	}


	public static void init() {
		entityManagerFactory = Persistence.createEntityManagerFactory(PERSISTENCE_UNIT_NAME);
	}


	public static synchronized String createUUID() {
		UUID uid = UUID.randomUUID();
		return uid.toString().toUpperCase();
	}

	//---------------------------------------------------------------------------------------------

	private EntityManager entityManager = null;
	private Stack<Object> transactionStack = new Stack<Object>();

	private StoreManager storeManager = null;
	private CategoryManager categoryManager = null;
	private PlatformManager platformManager = null;
	private DeviceManager deviceManager = null;
	private CurrencyManager currencyManager = null;
	private LocationManager locationManager = null;
	private ItemManager itemManager = null;
	

	public PersistenceContext(EntityManager entityManager) {
		this.entityManager = entityManager;
	}


	public void openTransaction(Object source) {
		
		if (transactionStack.isEmpty()) {
			entityManager.getTransaction().begin();
		}

		transactionStack.push(source);
	}

	
	public void commitTransaction(Object source) {
		
		transactionStack.pop();

		if (transactionStack.isEmpty()) {
			entityManager.getTransaction().commit();
		}

	}


	public void rollbackTransaction(Object source) {
		
		try {
			entityManager.getTransaction().rollback();
		} finally {
			transactionStack.clear();
		}
		
	}
	

	public <T> T run(TransactionalValueAction<T> action) throws MyMoneyException {
		try {

			openTransaction(null);
			T result = action.run();
			commitTransaction(null);
			return result;

		} catch (Exception e) {
			rollbackTransaction(null);
			throw new MyMoneyException(e);
		}
	}


	public void run(TransactionalAction action) throws MyMoneyException {
		try {

			openTransaction(null);
			action.run();
			commitTransaction(null);

		} catch (Exception e) {
			rollbackTransaction(null);
			throw new MyMoneyException(e);
		}
	}

	
	public void close() {

		try {
			if (entityManager == null) {
				return;
			}

			if (entityManager.getTransaction().isActive()) {
				entityManager.getTransaction().rollback();
			}

			entityManager.close();
			entityManager = null;
		} catch (Exception e) {
			e.printStackTrace();
		}
	}


	public EntityManager getEntityManager() {
		return entityManager;
	}

	
	public StoreManager getStoreManager() {
		if (storeManager == null) {
			storeManager = new StoreManager(this);
		}
		return storeManager;
	}
	
	public PlatformManager getPlatformManager() {
		if (platformManager == null) {
			platformManager = new PlatformManager(this);
		}
		return platformManager;
	}


	public DeviceManager getDeviceManager() {
		if (deviceManager == null) {
			deviceManager = new DeviceManager(this);
		}
		return deviceManager;
	}
	

	public CategoryManager getCategoryManager() {
		if (categoryManager == null) {
			categoryManager = new CategoryManager(this);
		}
		return categoryManager;
	}
	
	
	public CurrencyManager getCurrencyManager() {
		if (currencyManager == null) {
			currencyManager = new CurrencyManager(this);
		}
		return currencyManager;
	}
	
	
	public LocationManager getLocationManager() {
		if (locationManager == null) {
			locationManager = new LocationManager(this);
		}
		return locationManager;
	}
	
	public ItemManager getItemManager() {
		if (itemManager == null) {
			itemManager = new ItemManager(this);
		}
		return itemManager;
	}

}
